package com.java.advance.springmvc.servlet;

import com.java.advance.springmvc.annotation.*;
import com.java.advance.springmvc.controller.JamesController;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Created by aivin on 2019/4/2.
 */
public class DispatcherServlet extends HttpServlet {

    List<String> classNames = new ArrayList<>();

    Map<String, Object> beans = new HashMap<>(); //IOC容器

    Map<String, Object> handlerMapping = new HashMap<>();

    public DispatcherServlet() {

    }

    //指在tomcat启动时加载我们刚刚写的springmvc
    public void init(ServletConfig servletConfig) {
        try {
            //IOC容器；核心就是一个Map
            //扫描工程有多少个class
            //1、扫描com.java.advance.springmvc
            doScanPackage("com.java.advance.springmvc");

            doInstance(); //根据全类名创建bean，实例化bean

            doIoc();//根据bean进行依赖注入

            buildUrlMapping();//james/query -->method


        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

    }


    private void buildUrlMapping() {
        if (beans.entrySet().size() <= 0) {
            System.out.println("没有类的实例化");
            return;
        }
        for (Map.Entry<String, Object> entry : beans.entrySet()) {
            Object instance = entry.getValue();

            Class<?> clazz = instance.getClass();
            if (clazz.isAnnotationPresent(EnjoyController.class)) {
                EnjoyRequestMapping requestMapping = clazz.getAnnotation(EnjoyRequestMapping.class);
                String classPath = requestMapping.value();//类上面的james

                Method[] methods = clazz.getMethods();
                for (Method method : methods) {
                    if (method.isAnnotationPresent(EnjoyRequestMapping.class)) {
                        EnjoyRequestMapping requestMapping1 = clazz.getAnnotation(EnjoyRequestMapping.class);
                        String methodPath = requestMapping1.value(); //query

                        handlerMapping.put(classPath + methodPath, method); //路径与方法的关系

                    } else {
                        continue;
                    }
                }
            }

        }


    }

    //把Service注入到Controller
    public void doIoc() throws IllegalAccessException {
        if (beans.entrySet().size() <= 0) {
            System.out.println("没有一个实例化的类");
        }
        //把beans容器中所有的实例化的对象遍历出来
        for (Map.Entry<String, Object> entry : beans.entrySet()) {
            Object instance = entry.getValue();
            Class<?> clazz = instance.getClass();
            if (clazz.isAnnotationPresent(EnjoyController.class)) {
                Field[] fields = clazz.getDeclaredFields(); //获取所有属性
                for (Field field : fields) {
                    if (field.isAnnotationPresent(EnjoyAutowired.class)) {
                        EnjoyAutowired autowired = field.getAnnotation(EnjoyAutowired.class);
                        String key = autowired.value();//jamesService
                        field.setAccessible(true);//打开访问权限
                        field.set(instance, beans.get(key));//对成员变量进行设值


                    } else {
                        continue;
                    }

                }
            } else {
                continue;
            }

        }
    }

    //根据扫描的List全类名，进行实例化
    private void doInstance() {
        if (classNames.size() <= 0) {
            System.out.println("包扫描失败");
            return;
        }
        //遍历List中的所有的class类，对这些类进行实例化
        for (String className : classNames) {
            String cn = className.replace(".class", "");
            try {
                Class<?> clazz = Class.forName(cn);//com.java.advance.springmvc.JamesController
                if (clazz.isAnnotationPresent(EnjoyController.class)) {
                    Object instance = clazz.newInstance();//创建控制类
                    EnjoyRequestMapping enjoyRequestMapping = clazz.getAnnotation(EnjoyRequestMapping.class);
                    String rmvalue = enjoyRequestMapping.value();// james
                    beans.put(rmvalue, instance);

                } else if (clazz.isAnnotationPresent(EnjoyService.class)) {
                    EnjoyService enjoyService = clazz.getAnnotation(EnjoyService.class);
                    Object instce = clazz.newInstance();
                    String name = enjoyService.value();
                    beans.put(name, instce);
                } else {
                    continue;
                }


            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            }

        }
    }

    private void doScanPackage(String basePackage) {
        //class所在的目录
        URL url = this.getClass().getClassLoader().getResource("/" + basePackage.replaceAll("\\.", "/"));
        String fileStr = url.getFile();
        File file = new File(fileStr);
        String[] files = file.list();
        for (String path : files) {//扫描com.java.advance.springmvc
            File filepath = new File(fileStr + path);
            if (filepath.isDirectory()) {
                doScanPackage(basePackage + "." + path);
            } else {
                //得到class，全类名路径 com.java.advance.springmvc.controller.JamesController.class
                classNames.add(basePackage + "." + filepath.getName());
            }

        }
    }

    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req, resp);
    }


    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        try {
            super.doPost(req, resp);
            //获取请求路径 域名/james-mvc/james/query
            String uri = req.getRequestURI();
            String context = req.getContextPath();//james-mvc
            String path = uri.replace(context, "");// /james/query
            Method method = (Method) handlerMapping.get(path);
            //根据key = /james到map中去拿
            JamesController instance = (JamesController) beans.get("/" + path.split("/")[1]);

            Object arg[] = hand(req, resp, method);

            method.invoke(instance, arg);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }

    }

    //这里不是策略模式，VIP才有策略模式
    private Object[] hand(HttpServletRequest req, HttpServletResponse resp, Method method) {
        //拿到当前执行的方法有哪些参数
        Class<?>[] parameterTypes = method.getParameterTypes();
        //根据参数的个数，new一个参数的数组，将方法里的所有参数赋值到args来
        Object[] args = new Object[parameterTypes.length];

        int args_i = 0;
        int index = 0;
        for (Class<?> paramClazz : parameterTypes) {
            if (ServletRequest.class.isAssignableFrom(paramClazz)) {
                args[args_i++] = req;
            }
            if (ServletResponse.class.isAssignableFrom(paramClazz)) {
                args[args_i++] = resp;
            }
            //从0-3判断有没有RequestParam注解，很明显paramClazz为0和1时，不是
            //当为2和3时为@RequestParam,需要解析
            //[@com.enjoy.james.annotation.EnjoyRequestParam(value=name)]
            Annotation[] paramAns = method.getParameterAnnotations()[index];
            if (paramAns.length > 0) {
                for (Annotation paramAn : paramAns) {
                    if (EnjoyRequestParam.class.isAssignableFrom(paramAn.getClass())) {
                        EnjoyRequestParam requestParam = (EnjoyRequestParam) paramAn;
                        //找到注解里的name和age
                        args[args_i++] = req.getParameter(requestParam.value());
                    }
                }
            }
        }
        return args;
    }
}
